package com.duobaoyu.dby.supply.db.config;

import com.duobaoyu.dby.common.core.constant.CommonConstants;
import com.duobaoyu.dby.common.core.constant.StringPool;
import com.duobaoyu.dby.common.core.util.DateUtils;
import com.duobaoyu.dby.common.mybatis.base.BaseEntity;
import com.duobaoyu.dby.common.mybatis.plugins.AuthUtil;
import com.duobaoyu.dby.supply.db.util.ExecutorPluginUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Properties;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName CustomPlugin
 * @Description 自定义MySql拦截器插件
 * @createTime 10:51:00 2022/06/23
 */
@Intercepts({@Signature(
		type = StatementHandler.class,
		method = "prepare",
		args = {Connection.class, Integer.class}
)})
public class CustomPlugin implements Interceptor {

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
		BoundSql boundSql = (BoundSql) metaObject.getValue("boundSql");
		MetaObject boundSqlMo = SystemMetaObject.forObject(boundSql);
		MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
		Object params = boundSql.getParameterObject();
		if(ExecutorPluginUtils.isIgnoreTag(mappedStatement)){
			return invocation.proceed();
		}
		if(Objects.equals(mappedStatement.getSqlCommandType(), SqlCommandType.INSERT)){
			boolean insertFlag = false;
			if(!(params instanceof BaseEntity)){
				MapperMethod.ParamMap map = (MapperMethod.ParamMap) params;
				boolean mybatisPlusFlag = false;
				for (Object key : map.keySet()) {
					if(Objects.equals(key,"ew")){
						continue;
					}
					Object value = map.get(key);
					if(Objects.equals(key,"et") && ObjectUtils.isNotEmpty(value)){
						mybatisPlusFlag = true;
						break;
					}
				}
				if(!mybatisPlusFlag){
					insertFlag = true;
				}
				if(insertFlag){
					String originalSql = boundSql.getSql();
					String modifiedSql = insertAudit(originalSql);
					boundSqlMo.setValue("sql", modifiedSql);
				}
			}
		}
		if(Objects.equals(mappedStatement.getSqlCommandType(), SqlCommandType.UPDATE)){
			boolean updateFlag = false;
			if(!(params instanceof BaseEntity)){
				MapperMethod.ParamMap map= (MapperMethod.ParamMap) params;
				boolean mybatisPlusFlag = false;
				for (Object key : map.keySet()) {
					if(Objects.equals(key,"ew")){
						continue;
					}
					Object value = map.get(key);
					if(Objects.equals(key,"et") && ObjectUtils.isNotEmpty(value)){
						mybatisPlusFlag = true;
						break;
					}
				}
				if(!mybatisPlusFlag){
					updateFlag = true;
				}
			}
			if(updateFlag){
				String originalSql = boundSql.getSql();
				String modifiedSql = updateAudit(originalSql);
				boundSqlMo.setValue("sql", modifiedSql);
			}
		}
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return Interceptor.super.plugin(target);
	}

	@Override
	public void setProperties(Properties properties) {
		Interceptor.super.setProperties(properties);
	}


	private String insertAudit(String source){
		char[] chars = source.toCharArray();
		StringBuilder newSql=new StringBuilder();
		boolean afterFormalParam=false;
		for (char c : chars) {
			String cStr=String.valueOf(c);
			newSql.append(cStr);
			if (Objects.equals(StringPool.LEFT_BRACKET,cStr)){
				if(afterFormalParam){
					Long userId = AuthUtil.getUserId();
					String time = LocalDateTime.now().format(DateUtils.DEFAULT_DATE_TIME_FORMATTER);
					newSql.append(String.format("%d,%s,%d,%s,",userId,time,userId,time));
				}else {
					newSql.append("create_by,create_time,update_by,update_time,");
				}
			}
			if(Objects.equals(StringPool.RIGHT_BRACKET,cStr)){
				afterFormalParam=true;
			}
		}
		return newSql.toString();
	}

	private String updateAudit(String source){
		String[] strArray = source.split("\\s+");
		StringBuilder newSql=new StringBuilder();
		boolean afterSet=false;
		for (String s : strArray) {
			if(afterSet){
				s = String.format("update_by=%d,update_time='%s',",AuthUtil.getUserId(),LocalDateTime.now().format(DateUtils.DEFAULT_DATE_TIME_FORMATTER)) + s;
				StringBuilder sb = new StringBuilder(s);
				sb.insert(CommonConstants.ZERO,"version=version+1,");
				s = sb.toString();
				afterSet = false;
			}
			if(Objects.equals("set",s) || Objects.equals("SET",s)){
				afterSet = true;
			}
			newSql.append(s).append(StringPool.SPACE);
		}
		return newSql.toString();
	}
}
